package com.wzh.navcode.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.wzh.navcode.contants.NavCodeConstants;
import com.wzh.navcode.domain.vo.UserLoginVO;
import com.wzh.navcode.service.WxMsgService;
import com.wzh.navcode.websocket.WebSocketServer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.view.RedirectView;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Base64;

/**
 * @author wangzhenghai
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/wx/portal/public")
public class WxPortalController {
    private final WxMpService wxService;
    private final WxMpMessageRouter messageRouter;
    private final WxMsgService wxMsgService;

    @GetMapping(produces = "text/plain;charset=utf-8")
    public String authGet(@RequestParam(name = "signature", required = false) String signature, @RequestParam(name = "timestamp", required = false) String timestamp, @RequestParam(name = "nonce", required = false) String nonce, @RequestParam(name = "echostr", required = false) String echostr) {
        log.info("\n接收到来自微信服务器的认证消息：[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);
        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            throw new IllegalArgumentException("请求参数非法，请核实!");
        }
        if (wxService.checkSignature(timestamp, nonce, signature)) {
            return echostr;
        }
        return "非法请求";
    }

    @GetMapping("/callBack")
    public RedirectView callBack(@RequestParam String code) {
        try {
            WxOAuth2AccessToken accessToken = wxService.getOAuth2Service().getAccessToken(code);
            WxOAuth2UserInfo userInfo = wxService.getOAuth2Service().getUserInfo(accessToken, "zh_CN");
            // 登录
            StpUtil.login(userInfo.getOpenid());
            // 通知前端用户授权成功
            UserLoginVO build = UserLoginVO.builder()
                    .nickname(userInfo.getNickname())
                    .headImgUrl(userInfo.getHeadImgUrl())
                    .token(StpUtil.getTokenValue())
                    .build();
            WebSocketServer.notifyAuthSuccess(userInfo.getOpenid(), build);
            // 保存用户信息
            wxMsgService.save(userInfo);
        } catch (Exception e) {
            log.error("callBack error", e);
        }
        RedirectView redirectView = new RedirectView();
        redirectView.setUrl("https://www.wangzh.work/login-success");
        return redirectView;
    }

    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String post(@RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam("openid") String openid, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) {
        log.info("\n接收微信请求：[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}]," + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ", openid, signature, encType, msgSignature, timestamp, nonce, requestBody);

        if (!wxService.checkSignature(timestamp, nonce, signature)) {
            throw new IllegalArgumentException("非法请求，可能属于伪造的请求！");
        }

        String out = null;
        if (encType == null) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = this.route(inMessage);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toXml();
            String openId = inMessage.getFromUser();
            String uuid = inMessage.getEventKey();
            // 绑定用户
            bindUser(openId, uuid);
        } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpConfigStorage(), timestamp, nonce, msgSignature);
            log.debug("\n消息解密后内容为：\n{} ", inMessage.toString());
            WxMpXmlOutMessage outMessage = this.route(inMessage);
            if (outMessage == null) {
                return "";
            }
            out = outMessage.toEncryptedXml(wxService.getWxMpConfigStorage());
            String openId = inMessage.getFromUser();
            String uuid = inMessage.getEventKey();
            // 绑定用户
            bindUser(openId, uuid);
        }
        log.debug("\n组装回复信息：{}", out);
        return out;
    }

    /**
     * 绑定用户
     *
     * @param openId openId
     * @param uuid   uuid
     */
    private void bindUser(String openId, String uuid) {
        WebSocketServer.bindWxUser(openId, uuid);
    }

    private WxMpXmlOutMessage route(WxMpXmlMessage message) {
        try {
            return this.messageRouter.route(message);
        } catch (Exception e) {
            log.error("路由消息时出现异常！", e);
        }
        return null;
    }

    /**
     * 获取二维码
     *
     * @param uuid uuid
     * @return 二维码
     */
    @GetMapping("/getQrCode/{uuid}")
    public String getQrCode(@PathVariable String uuid) throws WxErrorException {
        String url = wxService.getQrcodeService().qrCodeCreateTmpTicket(uuid, NavCodeConstants.EXPIRE_SECONDS).getUrl();
        // 生成二维码并指定宽高
        BufferedImage generate = QrCodeUtil.generate(url, NavCodeConstants.QR_CODE_WIDTH, NavCodeConstants.QR_CODE_HEIGHT);
        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(generate, NavCodeConstants.QR_CODE_SUFFIX, os);
        } catch (IOException e) {
            return e.getMessage();
        }
        String encode = Base64.getEncoder().encodeToString(os.toByteArray());
        //如果二维码要在前端显示需要转成Base64
        return NavCodeConstants.BASE64_PREFIX + encode;
    }
}